#include <stdio.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>

#include "nio.h"

extern int errno;

static int getSocketPort(int socket){
	struct sockaddr_in address;
	socklen_t len = sizeof(address);
	if(getsockname(socket, (void*)&address, &len)){
		printf("get socketname failed: %s, and return\n", strerror(errno));
		exit(-1);
	}
	return address.sin_port;
}


static void dealWithFiredEvent(void * nio, struct FiredEvent * firedEvent, int* monitorFdLen){
	int socketFd = firedEvent->fd;
	printf("dealWithFiredEvent: %d  ", socketFd);
	int portNo = getSocketPort(socketFd);
	if(firedEvent->action & ACTION_READ){
		printf("has read event, read and close: %d\n", portNo);
		if(nioUnRegisteFd(nio, socketFd, ACTION_READ)){
			printf("nioUnRegisteFd failed: %s\n", strerror(errno));
			exit(-1);
		}
		*monitorFdLen -= 1;
		char buffer[1024];
		int recvNo = recv(socketFd, buffer, 1024, 0);
		if(recvNo <= 0){
			printf("recv failed: %d\n", recvNo);
			exit(-1);
		}
		printf("recv message: %s\n", buffer);
		if(close(socketFd)){
			printf("close socket %d failed", socketFd);
			exit(-1);
		}
		return;
	}
	if(firedEvent->action & ACTION_WRITE){
		printf("socketFd writable, and write Hello\n");
		char hello[50];
		sprintf(hello, "HelloWorld: %d\n", portNo);
		send(socketFd, hello, strlen(hello), 0);
		if(nioUnRegisteFd(nio, socketFd, ACTION_WRITE)){
			printf("nioUnRegisteFd failed\n");
			exit(-1);
		}
	}
}

int main(){
	printf("demo start: \n");
	void* nio = nioInit();
	if(!nio){
		printf("nioInit failed\n");
		exit(-1);
	}
	int monitorFdLen = 10;
	for(int i = 0; i < monitorFdLen; i++){
		int socketFd = socket(PF_INET, SOCK_STREAM, 0);
		if(socketFd < 0){
			printf("socketFd init falied: %d\n", socketFd);
			exit(-1);
		}
		struct sockaddr_in serverAddress;
		memset(&serverAddress, 0, sizeof(serverAddress));
		serverAddress.sin_family = AF_INET;
		serverAddress.sin_port = htons(8080);
		if(inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr) <= 0){
			perror("Invalid address or Address not supoorted\n");
			exit(-1);
		}
		printf("socket connecting ....\n");
		int connectResult = connect(socketFd, (void *) &serverAddress, sizeof(serverAddress));
		if ( connectResult != 0){
			printf("Connected Failed, error code: %d, error description: %s. \n", errno, strerror(errno));
			exit(-1);
		}
		printf("socket connected.\n");
		int result;
		if((result = nioRegisteFd(nio, socketFd, ACTION_READ | ACTION_WRITE))){
			printf("nioRegisteFd failed: %d, %s\n", result, strerror(errno));
			exit(-1);
		}
	}
	int firedEventLen = 5;
	int index = 0;
	struct FiredEvent firedEvents[firedEventLen];
	while(1){
		printf("wait until something you are interested happed....\n");
		int wait = nioWait(nio, firedEvents, firedEventLen);
		if(wait < 0){
			printf("nioWait failed\n");
			exit(-1);
		}
		printf("get change event, and wait is %d.\n", wait);
		for(index = 0; index < wait; index++){
			struct FiredEvent firedEvent = firedEvents[index];
			dealWithFiredEvent(nio, &firedEvent, &monitorFdLen);
		}
		if(monitorFdLen == 0){
			printf("monitorEvent is zero now, and break\n");
			break;
		}
	}
	nioClose(nio);
	printf("demo end\n");
}
